// 
// $Id$
//
// Implementation of URL_HOOK class
//
// <Nitin Karkhanis> <<nkarkhan@yahoo.com>>
// Created on: <05-June-2007 14:55:24 nitin_karkhanis>
//
// Copyright (C) <Nitin Karkhanis>.  All rights reserved.
//

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <UrlHook.hh>

UrlHook::UrlHook (unsigned short port)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	server_port = port;
	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
    /* Tell the user that we couldn't find a useable */
    /* winsock.dll.                                  */
        cout << __FUNCTION__ << " failed " << err << endl;
        return;
	}

/* Confirm that the Windows Sockets DLL supports 1.1.*/
/* Note that if the DLL supports versions greater    */
/* than 1.1 in addition to 1.1, it will still return */
/* 1.1 in wVersion since that is the version we      */
/* requested.                                        */

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		 HIBYTE( wsaData.wVersion ) != 1 ) {
    /* Tell the user that we couldn't find a useable */
    /* winsock.dll.                                  */
        cout << __FUNCTION__ << " failed, Incorrect version" << endl;
		WSACleanup( );
		return;
	}
    
    struct sockaddr_in my_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
//        printf("socket %s", strerror(errno));
		WSACleanup( );
        return;
    }

    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(port);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    memset(&(my_addr.sin_zero), '\0', 8);

    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))
        == -1) {
//        printf("bind %s", strerror(errno));
		WSACleanup( );
        return;
    }

#define BACKLOG 10
    if (listen(sockfd, BACKLOG) == -1) {
//        printf("listen %s", strerror(errno));
		WSACleanup( );
        return;
    }
//    printf("WebServer running\n");
	gethostname(hostname, sizeof(hostname));
	return;
}

int
UrlHook::set_url_cb (char *url, TypedCallbackInterface *cb)
{
    cmd_cb_t *save_cb;

    save_cb = new cmd_cb_t;

    memset(save_cb->url_str, 0, sizeof(save_cb->url_str));
    memset(save_cb->url_param, 0, sizeof(save_cb->url_param));
	strcpy(save_cb->url_str, url);
    save_cb->cb = cb;
    if (NULL == cb) {
//        printf("Null cb??!\n");
        return -1;
    }
    cb_vec.push_back(save_cb);
    return 0;
}

int
UrlHook::dispatch_url_cb (char *rcv_str)
{
    char *get_cmd;

    // Scan for command which shows up just after GET
    get_cmd = strstr(rcv_str, "GET");
    if (NULL == get_cmd) {
//        printf("Get not found <%s>\n", rcv_str);
        return 0;
    }

    // Go past GET
    get_cmd = get_cmd + 4;
    // Iterate thru all cbs and see if any match up
    
    for (unsigned int idx = 0; idx < cb_vec.size(); idx ++) {
        cmd_cb_t *current_cb;
        current_cb = cb_vec.at(idx);
        if (strstr(get_cmd, &(current_cb->url_str[0]) )) {
//            printf("Cmd match found\n");
            // Move forward by size of url
            get_cmd = get_cmd + strlen(current_cb->url_str) + 1;
            strncpy(current_cb->url_param,
                    get_cmd,
                    (strlen(get_cmd) > sizeof(current_cb->url_param)) ?
                    sizeof(current_cb->url_param) : strlen(get_cmd));
            // Call the Call back !
//            printf("Call registered Callback\n");
            (*current_cb->cb)();
            return 0;
        }
    }
//    printf("Cmd match NOT found\n");
    return 0;
}

int
UrlHook::reply_browser (char *reply, bool refresh, int time)
{
	char refresh_str[500];
    char r_str[500];

	sprintf(refresh_str, HTML_REPLY_REFRESH, time, hostname, server_port);
	sprintf(r_str, HTML_REPLY_OK_PRE, (true == refresh) ? refresh_str : " ", reply, HTML_REPLY_OK_POST);
    if (send(currentfd, r_str, (int)strlen(r_str), 0) == -1) {
//        printf("%s:%s","send", strerror(errno));
    }
    return 0;
}

int
UrlHook::evt_loop (void)
{
    int sin_size;
    struct sockaddr_in their_addr;
    
    for (;;) {
        sin_size = sizeof(struct sockaddr_in);
        if ((currentfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size))
            == -1) {
            return -1;
        }
//        printf("server: got connection from %s",inet_ntoa(their_addr.sin_addr));
        char rcv_str[800];
        int rcv_len;
            
        memset(rcv_str, 80, 0);
        if ((rcv_len = recv(currentfd, rcv_str, sizeof(rcv_str), 0)) == -1) {
//            printf("%s:%s","recv", strerror(errno));
            return -1;
        }
        rcv_str[rcv_len - 2] = 0;
//        printf("Receive Str:%d:<%s>\n", rcv_len, rcv_str);
 
        dispatch_url_cb(rcv_str);
        closesocket(currentfd);
    }
}
